#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
#include <list>
#include "pcap.h"
#include "ncp_cap.h"
#include "ncp_parse.h"
#include "ncp_rule.h"

using namespace std;

/*=============================================================*/
/* define                                                      */
/*=============================================================*/
/* wait time */
#define TIME_OUT 1000L

/*=============================================================*/
/* task struct                                                 */
/*=============================================================*/
/* task struct */
typedef struct struct_task
{
	pcap_pkthdr *header;
	u_char      *pkt_data;
}struct_task;

/* task queue */
typedef list<struct_task*> task_queue_list;
task_queue_list task_queue;

/*=============================================================*/
/* task control                                                */
/*=============================================================*/
/* exit flag */
bool has_init                 = false;
bool will_exit                = false;

/* thread control */
static CRITICAL_SECTION critical_task;
       CRITICAL_SECTION critical_table;
       CRITICAL_SECTION critical_rule;
static HANDLE semaphore_producer     = NULL;
static HANDLE semaphore_consumer     = NULL;
static HANDLE thread_id_cap   = NULL;
static HANDLE thread_id_parse = NULL;

/*=============================================================*/
/*                                                             */
/*=============================================================*/
char errbuf[PCAP_ERRBUF_SIZE];

/*=============================================================*/
/* function define                                             */
/*=============================================================*/

/* do start and stop */
static bool do_start(def_session_struct* session_struct);
static bool do_stop();

/* task control */
static struct_task* create_task(pcap_pkthdr *header, const u_char *pkt_data);
static bool         release_task(struct_task* task);
static bool         addto_task_queue(struct_task* tk);
static struct_task* get_tack_from_queue();
static void         clear_queue();

/* producer and consumer work function */
unsigned int __stdcall work_cap(void* param);
unsigned int __stdcall work_parse(void* param);

/*=============================================================*/
/* function implement                                          */
/*=============================================================*/
struct_task* create_task(pcap_pkthdr *header, const u_char *pkt_data)
{
	struct_task* task        = new struct_task();
	pcap_pkthdr* task_head   = new pcap_pkthdr();
	unsigned char* task_data = new unsigned char[header->caplen];

	if((task == NULL) || (task_head == NULL) || (task_data == NULL))
	{
		delete task;
		delete task_head;
		delete task_data;
		return NULL;
	}

	task_head->ts     = header->ts;
	task_head->caplen = header->caplen;
	task_head->len    = header->len;

	memcpy(task_data, pkt_data, header->caplen);

	task->header   = task_head;
	task->pkt_data = task_data;

	return task;
}

bool release_task(struct_task* task)
{
	delete task->header;
	delete task->pkt_data;
	delete task;
	return true;
}

void clear_queue()
{
	while(task_queue.size() > 0)
	{
		release_task(task_queue.front());		
		task_queue.pop_front();		
	}
	
	task_queue.clear();
}

bool addto_task_queue(struct_task* tk)
{
	__try 
	{
		EnterCriticalSection(&critical_task);    
		task_queue.push_back(tk);		

#if LOG_LEVEL_FIV >= LOG_CUR_LEVEL		
		fprintf(g_log_io, "(producer count=%d)\n", task_queue.size());
#endif

	}	
	__finally 
	{    
		LeaveCriticalSection(&critical_task);
	}
	
	return true;
}

struct_task* get_tack_from_queue()
{
	struct_task* task = NULL;

	__try 
	{
		EnterCriticalSection(&critical_task);    
		task = task_queue.front();		
		task_queue.pop_front();		

#if LOG_LEVEL_FIV >= LOG_CUR_LEVEL		
		fprintf(g_log_io, "(consumer count=%d)\n", task_queue.size());
#endif
	}	
	__finally 
	{    
		LeaveCriticalSection(&critical_task);
	}

	return task;
}

unsigned int __stdcall work_cap(void* param)
{
	pcap_t *fp                 = (pcap_t *)param;
	struct pcap_pkthdr *header = NULL;
	const u_char *pkt_data     = NULL;
	int res                    = 0;

	while(!will_exit)
	{
		switch(WaitForSingleObject(semaphore_producer, TIME_OUT))
		{
		case WAIT_OBJECT_0:	
		case  WAIT_TIMEOUT:
			res = pcap_next_ex( fp, &header, &pkt_data);
			if(res > 0)
			{
				struct_task* task = create_task(header, pkt_data);
				if(NULL == task)
				{
					do_stop();
					return 0;
				}
				if(!addto_task_queue(task))
				{
				}
				ReleaseSemaphore(semaphore_consumer, 1, NULL);				
			}

			if(res == 0)
			{
				continue;
			}

			if(res < 0)
			{
				pcap_close(fp);
				do_stop();
				return 0;
			}			
			break;
//		case WAIT_TIMEOUT:
//			continue;
		case WAIT_FAILED:
		default:
			pcap_close(fp);
			do_stop();
			return 0;
		}		
	}

	pcap_close(fp);

#if LOG_LEVEL_THR >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_CAP_EXIT);
#endif

	return 0;
}

unsigned int __stdcall work_parse(void* param)
{
	pcap_t *fp        = (pcap_t *)param;
	struct_task* task = NULL;
		
	while(!will_exit)
	{
		switch(WaitForSingleObject(semaphore_consumer, TIME_OUT))
		{
		case WAIT_OBJECT_0:			
			task = get_tack_from_queue();
			ReleaseSemaphore(semaphore_producer, 1, NULL);
			if(NULL == task)
			{
				continue;
			}

			parse_packet(task->header, task->pkt_data);
			release_task(task);
			break;
		case WAIT_TIMEOUT:
			continue;
		case WAIT_FAILED:
		default:
			do_stop();
			return 0;	
		}		
	}

#if LOG_LEVEL_THR >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_PARSE_EXIT);
#endif

	return 0;
}

bool do_start(def_session_struct* session_struct)
{
	if(has_init)
	{
		return false;
	}
	/* log init */	
	if(!log_init_io(session_struct->log_io_type, session_struct->log_file_name))
	{
		return false;
	}

#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
	fprintf(g_log_io, LOG_SESSION_START);
	log_stamp();
#endif

	/* cap init */
	pcap_t *adhandle;
	
	if((adhandle= pcap_open(session_struct->device_name, session_struct->snaplen, session_struct->flags, session_struct->timeout, NULL, errbuf)) == NULL)
	{
#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_CAP_INIT_FAILD);
#endif
		log_close_io();
		return false;
	}	

#if LOG_LEVEL_TWO >= LOG_CUR_LEVEL
	fprintf(g_log_io, LOG_CAP_INIT_OK);
#endif

	if(pcap_datalink(adhandle) != DLT_EN10MB)
	{
#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_CAP_LINK_FAILD);
#endif
		pcap_close(adhandle);
		log_close_io();
		return false;
	}

	if(NULL != session_struct->filter_text)
	{
#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_CAP_FILTER, session_struct->filter_text);
#endif
		u_int netmask;    
		bpf_program fcode; 

		//if(adhandle->addresses != NULL) 		
		//	netmask=((struct sockaddr_in *)(adhandle->addresses->netmask))->sin_addr.S_un.S_addr; 
		//else 			
			netmask=0xffffff;  
	  
		if(pcap_compile(adhandle, &fcode, session_struct->filter_text, 1, netmask) <0 )
		{
#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
			fprintf(g_log_io, LOG_CAP_COMPILE_ERROR);
#endif						
			pcap_close(adhandle);
			log_close_io();
			return false; 
		} 
	
		if(pcap_setfilter(adhandle, &fcode)<0)
		{ 
#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
			fprintf(g_log_io, LOG_CAP_SET_FILTER_ERROR);
#endif			
			pcap_close(adhandle);
			log_close_io();
			return false; 
		} 

	}

#if LOG_LEVEL_TWO >= LOG_CUR_LEVEL
	fprintf(g_log_io, LOG_CAP_LINK_OK);	
#endif

	/* rule init */
	if(!rule_create_vector())
	{
#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_RULE_CREATE_FAILD);	
#endif
		pcap_close(adhandle);
		log_close_io();
		return false;
	}

#if LOG_LEVEL_TWO >= LOG_CUR_LEVEL
	fprintf(g_log_io, LOG_RULE_CREATE_OK);
#endif

	/* thread init */
	InitializeCriticalSection(&critical_task); 
	InitializeCriticalSection(&critical_rule); 
	InitializeCriticalSection(&critical_table); 

	will_exit          = false;	
	semaphore_producer = CreateSemaphore(NULL, session_struct->max_task_count, session_struct->max_task_count, NULL);
	semaphore_consumer = CreateSemaphore(NULL, 0, session_struct->max_task_count, NULL);
	unsigned tid;
	thread_id_cap      = (HANDLE)_beginthreadex(NULL, 0, work_cap,   adhandle, 0, &tid);	
	thread_id_parse    = (HANDLE)_beginthreadex(NULL, 0, work_parse, adhandle, 0, &tid);
	

	if(NULL == thread_id_cap || NULL == thread_id_parse || NULL == semaphore_producer || NULL == semaphore_consumer)
	{
#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_THREAD_FAILD);
#endif
		will_exit = true;
		pcap_close(adhandle);
		DeleteCriticalSection(&critical_task);
		DeleteCriticalSection(&critical_rule);
		DeleteCriticalSection(&critical_table);
		CloseHandle(semaphore_producer);
		CloseHandle(semaphore_consumer);
		rule_free_vector();
		log_close_io();		
		return false;
	}

#if LOG_LEVEL_TWO >= LOG_CUR_LEVEL
	fprintf(g_log_io, LOG_THREAD_OK);
#endif

#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
		fprintf(g_log_io, LOG_SESSION_OK);
#endif

	log_flush_io();
	has_init = true;	
	
	return true;
}

bool do_stop()
{
	if(!has_init)
	{
		return false;
	}	
	will_exit = true;	
	WaitForSingleObject(thread_id_cap,   INFINITE);	
	WaitForSingleObject(thread_id_parse, INFINITE);	
	clear_queue();
	DeleteCriticalSection(&critical_task);
	DeleteCriticalSection(&critical_rule);
	DeleteCriticalSection(&critical_table);
	CloseHandle(semaphore_producer);
	CloseHandle(semaphore_consumer);
	rule_free_vector();	

#if LOG_LEVEL_ONE >= LOG_CUR_LEVEL
	fprintf(g_log_io, LOG_SESSION_END);
#endif
	log_close_io();
	has_init = false;
	return true;
}

bool ncp_cap_start_cap(def_session_struct* session_struct)
{
	return do_start(session_struct);	
}

bool ncp_cap_stop_cap()
{	
	return do_stop();
}